package com.fa4j.system.settings.service.impl;

import com.fa4j.common.base.exception.CommonRequestError;
import com.fa4j.common.base.model.CommonConstants;
import com.fa4j.common.base.model.CommonState;
import com.fa4j.common.base.model.RemoveCmd;
import com.fa4j.common.base.util.ValidUtil;
import com.fa4j.system.settings.convert.DictConverter;
import com.fa4j.system.settings.model.*;
import com.fa4j.system.settings.repository.DictRepository;
import com.fa4j.system.settings.service.DictService;
import jakarta.validation.Valid;
import jakarta.validation.Validator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.*;
import java.util.stream.Stream;

/**
 * 系统字典 服务接口实现
 *
 * @since 2025-06-12 16:58:22
 */
@Slf4j
@Service
@Validated
@RequiredArgsConstructor
public class DictServiceImpl implements DictService {

    private final DictRepository dictRepository;
    private final DictConverter dictConverter;

    private final Validator validator;

    /**
     * 新增系统字典
     *
     * @param cmd 新增参数
     * @return 系统字典
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Dict create(@Valid DictCreateCmd cmd) {
        Dict parent = null;
        if (!Objects.equals(cmd.getParentId(), CommonConstants.TREE_ROOT_ID)) {
            parent = dictRepository.findById(cmd.getParentId())
                    .orElseThrow(() -> CommonRequestError.NOT_FOUND.build("上级字典"));
            Dict root = Objects.equals(parent.getParentId(), CommonConstants.TREE_ROOT_ID)
                    ? parent
                    : dictRepository.findById(parent.getRootId())
                    .orElseThrow(() -> CommonRequestError.NOT_FOUND.build("根级字典"));
            if ((parent.getLevel() + 1) > root.getMaxLevel()) {
                throw DictError.DICT_LEVEL_LIMIT.build(root.getMaxLevel());
            }
            if (root.getValueType() == DictValueType.CUSTOM) {
                validCustomData(root.getCustomFields(), cmd.getCustomData());
            }
            cmd.setValueType(root.getValueType());
            cmd.setMaxLevel(root.getMaxLevel());
        } else {
            // 根字典
            ValidUtil.valid(validator, cmd, DictCreateCmd.ValidRootDict.class);
            if (cmd.getValueType() == DictValueType.CUSTOM) {
                ValidUtil.validNotEmpty("自定义字段", cmd.getCustomFields());
            }
        }
        if (dictRepository.findByParentIdAndCode(cmd.getParentId(), cmd.getDictCode()).isPresent()) {
            throw CommonRequestError.EXISTED.build("编码" + cmd.getDictCode());
        }
        Dict entity = dictConverter.toEntity(cmd);
        entity.setIsLeaf(true);
        if (parent != null) {
            entity.setParentId(parent.getId());
            entity.setLevel(parent.getLevel() + 1);
            entity.setRootId(Objects.equals(parent.getParentId(), CommonConstants.TREE_ROOT_ID) ? parent.getId() : parent.getRootId());
            if (Boolean.TRUE == parent.getIsLeaf()) {
                parent.setIsLeaf(false);
                dictRepository.modify(parent);
            }
        } else {
            entity.setRootId(CommonConstants.TREE_ROOT_ID);
            entity.setParentId(CommonConstants.TREE_ROOT_ID);
            entity.setLevel(1);
        }
        dictRepository.create(entity);
        return entity;
    }

    /**
     * 验证自定义字段
     */
    private void validCustomData(List<DictCustomField> customFields, Map<String, Object> customData) {
        for (DictCustomField field : customFields) {
            Object data = customData == null ? null : customData.get(field.getName());
            if (field.getNullable() == null || !field.getNullable()) {
                ValidUtil.validNotEmpty(field.getTitle(), data);
            }
            if (data != null) {
                if (field.getType() == DictCustomFieldType.BOOLEAN && !(data instanceof Boolean)) {
                    throw CommonRequestError.PARAM_TYPE_ERROR.build(field.getTitle());
                }
                if (field.getType() == DictCustomFieldType.NUMBER && !(data instanceof Number)) {
                    throw CommonRequestError.PARAM_TYPE_ERROR.build(field.getTitle());
                }
            }

        }
    }

    /**
     * 编辑系统字典
     *
     * @param cmd 编辑参数
     * @return 系统字典
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Dict modify(@Valid DictModifyCmd cmd) {
        Dict entity = dictRepository.findById(cmd.getId())
                .orElseThrow(() -> CommonRequestError.NOT_FOUND.build("系统字典"));
        if (entity.getValueType() == DictValueType.CUSTOM && (cmd.getCustomFields() != null || cmd.getCustomData() != null)) {
            if (Objects.equals(entity.getParentId(), CommonConstants.TREE_ROOT_ID)) {
                //根节点
                ValidUtil.validNotEmpty("自定义字段", cmd.getCustomFields());
            } else {
                Dict root = dictRepository.findById(entity.getRootId())
                        .orElseThrow(() -> CommonRequestError.NOT_FOUND.build("根级字典"));
                validCustomData(root.getCustomFields(), cmd.getCustomData());
            }
        }
        dictConverter.updateEntity(entity, cmd);
        dictRepository.modify(entity);
        return entity;
    }

    /**
     * 删除系统字典
     *
     * @param cmd 删除参数
     * @return 系统字典
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Dict remove(@Valid RemoveCmd<Integer> cmd) {
        Dict entity = dictRepository.findById(cmd.getId())
                .orElseThrow(() -> CommonRequestError.NOT_FOUND.build("系统字典"));
        if (dictRepository.countByParentId(cmd.getId()) > 0) {
            throw DictError.DICT_EXIST_CHILDREN.build();
        }
        entity.setOperatorId(cmd.getOperatorId());
        dictRepository.remove(entity);
        return entity;
    }

    /**
     * 批量保存字典项
     *
     * @param cmd 批量参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchSave(@Valid DictBatchSaveCmd cmd) {
        Dict rootDict = dictRepository.findByParentIdAndCode(CommonConstants.TREE_ROOT_ID, cmd.getRootCode()).orElse(null);
        List<Dict> existList = new ArrayList<>();
        if (rootDict == null) {
            rootDict = buildRootDict(cmd);
        } else {
            existList = Stream.concat(
                    dictRepository.queryListByRootId(rootDict.getId(), null).stream(),
                    Stream.of(rootDict)
            ).toList();
        }
        List<Dict> dictList = buildDistList(cmd, rootDict);
        Map<String, Dict> parentMap = buildDistRarentMap(cmd, rootDict, existList, dictList);
        Set<Integer> ignoreIds = new HashSet<>();
        for (Dict dict : dictList) {
            saveParent(dict, parentMap, existList, ignoreIds);
            saveDict(dict, parentMap, existList, ignoreIds);
        }
    }

    private Dict buildRootDict(@Valid DictBatchSaveCmd cmd) {
        Dict dict = new Dict();
        dict.setDictName(cmd.getRootName());
        dict.setDictCode(cmd.getRootCode());
        dict.setValueType(cmd.getValueType());
        dict.setState(CommonState.ENABLE);
        dict.setParentId(CommonConstants.TREE_ROOT_ID);
        dict.setRootId(CommonConstants.TREE_ROOT_ID);
        dict.setPriority(1);
        dict.setLevel(1);
        dict.setMaxLevel(cmd.getMaxLevel());
        dict.setIsLeaf(false);
        dict.setCustomFields(cmd.getCustomFields());
        return dict;
    }

    private void saveParent(Dict child, Map<String, Dict> parentMap, List<Dict> existList, Set<Integer> ignoreIds) {
        Dict parent = parentMap.get(child.getDictCode());
        if (parent.getParentId() == null) {
            saveParent(parent, parentMap, existList, ignoreIds);
        }
        saveDict(parent, parentMap, existList, ignoreIds);
        child.setLevel(parent.getLevel() + 1);
        child.setParentId(parent.getId());
        child.setRootId(Objects.equals(parent.getRootId(), CommonConstants.TREE_ROOT_ID) ? parent.getId() : parent.getRootId());
    }

    private void saveDict(Dict dict, Map<String, Dict> parentMap, List<Dict> existList, Set<Integer> ignoreIds) {
        if (!ignoreIds.contains(dict.getId())) {
            if (dict.getLevel() > dict.getMaxLevel()) {
                throw DictError.DICT_LEVEL_LIMIT.build(dict.getMaxLevel());
            }
            dict.setIsLeaf(parentMap.values().stream().noneMatch(p -> Objects.equals(p.getDictCode(), dict.getDictCode())));
            Optional<Dict> exist = existList.stream().filter(e -> Objects.equals(e.getDictCode(), dict.getDictCode())).findFirst();
            if (exist.isPresent()) {
                dict.setId(exist.get().getId());
                if (checkDiff(exist.get(), dict)) {
                    dictRepository.modify(dict);
                }
            } else {
                dictRepository.create(dict);
            }
            ignoreIds.add(dict.getId());
        }
    }

    private boolean checkDiff(Dict exist, Dict dict) {
        if (!Objects.equals(exist.getLevel(), dict.getLevel())) {
            throw CommonRequestError.BAD_REQUEST.build(dict.getDictCode() + "层级发生变化，无法批量编辑", null);
        }
        if (dict.getParentId() != null && !Objects.equals(exist.getParentId(), dict.getParentId())) {
            throw CommonRequestError.BAD_REQUEST.build(dict.getDictCode() + "上级ID发生变化，无法批量编辑", null);
        }
        return !Objects.equals(exist.getCustomData(), dict.getCustomData())
                || !Objects.equals(exist.getDictValue(), dict.getDictValue())
                || !Objects.equals(exist.getIsLeaf(), dict.getIsLeaf())
                || !Objects.equals(exist.getPriority(), dict.getPriority());
    }


    private Map<String, Dict> buildDistRarentMap(DictBatchSaveCmd cmd, Dict rootDict, List<Dict> existList, List<Dict> dictList) {
        Map<String, Dict> map = new HashMap<>();
        for (DictBatchSaveCmd.Item item : cmd.getItems()) {
            map.put(item.dictCode(), findParentDict(item.parentCode(), rootDict, existList, dictList));
        }
        return map;
    }

    /**
     * 查询父级字典
     */
    private Dict findParentDict(String parentCode, Dict rootDict, List<Dict> existList, List<Dict> dictList) {
        if (rootDict.getDictCode().equals(parentCode)) {
            return rootDict;
        }
        return dictList.stream()
                .filter(e -> Objects.equals(e.getDictCode(), parentCode))
                .findFirst()
                .or(() ->
                        existList.stream()
                                .filter(e -> Objects.equals(e.getDictCode(), parentCode))
                                .findFirst()
                                .map(dictConverter::clone)
                )
                .orElseThrow(() -> CommonRequestError.NOT_FOUND.build("上级字典" + parentCode));
    }

    private List<Dict> buildDistList(@Valid DictBatchSaveCmd cmd, Dict rootDict) {
        List<Dict> result = new LinkedList<>();
        Set<String> existCodeSet = new HashSet<>();
        for (DictBatchSaveCmd.Item item : cmd.getItems()) {
            if (rootDict.getValueType() == DictValueType.CUSTOM) {
                validCustomData(rootDict.getCustomFields(), item.customData());
            }
            if (!existCodeSet.add(item.dictCode())) {
                throw CommonRequestError.EXISTED.build("编码" + item.dictCode());
            }
            Dict dict = new Dict();
            dict.setDictName(item.dictName());
            dict.setDictCode(item.dictCode());
            dict.setPriority(item.priority());
            dict.setDictValue(item.dictValue());
            dict.setCustomData(item.customData());
            dict.setState(CommonState.ENABLE);
            dict.setValueType(rootDict.getValueType());
            dict.setRootId(rootDict.getId());
            dict.setMaxLevel(rootDict.getMaxLevel());
            dict.setOperatorId(cmd.getOperatorId());
            result.add(dict);
        }
        return result;
    }


}




